const CryptoJS = require("crypto-js");

class Block {
  /**
   * @param {*} index
   * first block:0
   * @param {*} previousHash
   * explicity determines the previous block
   * @param {*} timestamp
   * @param {*} data
   * anything that the finder of the block wants to include in the blockchain
   * @param {*} hash
   * taken from the content of the block
   */
  constructor(index, previousHash, timestamp, data, hash) {
    this.index = index;
    this.previousHash = previousHash.toString();
    this.timestamp = timestamp;
    this.data = data;
    this.hash = hash.toString();
  }
}

/**
 * 散列单元
 * @param {*} index
 * @param {*} previousHash
 * explicity determines the previous block
 * @param {*} timestamp
 * @param {*} data
 * anything that the finder of the block wants to include in the blockchain
 */
const calculateHash = (index, previousHash, timestamp, data) => {
  return CryptoJS.SHA256(index + previousHash + timestamp + data).toString();
};

/**
 * 计算散列
 * @param {*} block
 */
const calculateHashForBlock = block => {
  return calculateHash(
    block.index,
    block.previousHash,
    block.timestamp,
    block.data
  );
};

const getGenesisBlock = () => {
  return new Block(
    0,
    "0",
    1465154705,
    "my genesis block!!",
    "816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7"
  );
};

// 创世纪块
const blockchain = [getGenesisBlock()];

const getLatestBlock = () => blockchain[blockchain.length - 1];

/**
 * 产生单元
 * @param {*} blockData
 */
const generateNextBlock = blockData => {
  const previousBlock = getLatestBlock();
  const nextIndex = previousBlock.index + 1;
  const nextTimestamp = new Date().getTime() / 1000;
  const nextHash = calculateHash(
    nextIndex,
    previousBlock.hash,
    nextTimestamp,
    blockData
  );
  return new Block(
    nextIndex,
    previousBlock.hash,
    nextTimestamp,
    blockData,
    nextHash
  );
};

/**
 * 确认块的完整性
 * @param {*} newBlock
 * @param {*} previousBlock
 */
const isValidNewBlock = (newBlock, previousBlock) => {
  if (previousBlock.index + 1 !== newBlock.index) {
    console.log("invalid index");
    return false;
  } else if (previousBlock.hash !== newBlock.previousHash) {
    console.log("invalid previoushash");
    return false;
  } else if (calculateHashForBlock(newBlock) !== newBlock.hash) {
    console.log(
      typeof newBlock.hash + " " + typeof calculateHashForBlock(newBlock)
    );
    console.log(
      "invalid hash: " + calculateHashForBlock(newBlock) + " " + newBlock.hash
    );
    return false;
  }
  return true;
};

/**
 *
 * @param {*} newBlock
 */
const addBlock = newBlock => {
  if (isValidNewBlock(newBlock, getLatestBlock())) {
    blockchain.push(newBlock);
  }
};

console.log(blockchain);
addBlock(generateNextBlock("测试"));
console.log("-----------");
console.log(blockchain);
